﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
using System.Collections;
using MapControl.Misc.DAL;

namespace MapControl.Misc
{
    public class ComPort
    {
        private SerialPort serialPort;
        public List<string> ListMessages = new List<string>();
        //public List<BaseGPSPoint> gpsPointList;
        private string _baudRate = string.Empty;
        private string _stopBits = string.Empty;
        private string _dataBits = string.Empty;
        private string _parity = string.Empty;
        private string _portName = string.Empty;

        //variable for sending file
        private string filename;
        //private byte[] buffer;
        private int _BytesToRead;
        //private int count=0;

        //Dữ liệu đọc từ cổng COM
        private int MAX_LENGTH = 4096; //Len tối đa đọc 1 buffer
        private byte[] comBuff; // Dữ liệu lấy từ cổng COM dùng để xử lý
        private int lenBuff = 0; //Len của buffer

        private bool isFirstReceivedData = false;

        #region Constructor

        public ComPort()
        {
            
        }
        public ComPort(string portName, string baudRate, string stopBits, string dataBits, string parity, bool isText)
        {
            //khởi tạo
            _baudRate = baudRate;
            _stopBits = stopBits;
            _dataBits = dataBits;
            _parity = parity;
            _portName = portName;

            serialPort = new SerialPort();
            serialPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
            serialPort.ErrorReceived+=new SerialErrorReceivedEventHandler(comPort_ErrorReceived);
        }
        #endregion

        #region Properties
        public string FileName
        {
            set { filename = value; }
            get { return filename; }
        }

        //public int Count
        //{
        //    set { count = value; }
        //    get { return count; }
        //}

        //public byte[] BufferByteArr
        //{
        //    set { buffer = value; }
        //    get { return buffer; }
        //}

        public int BytesToRead
        {
            set { _BytesToRead = value; }
            get { return _BytesToRead; }
        }

        public string BaudRate
        {
            set { _baudRate = value; }
            get { return _baudRate; }
        }
        public string StopBits
        {
            set { _stopBits = value; }
            get { return _stopBits; }
        }
        public string DataBits
        {
            set { _dataBits = value; }
            get { return _dataBits; }
        }
        public string Parity
        {
            set { _parity = value; }
            get { return _parity; }
        }
        public string PortName
        {
            set { _portName = value; }
            get { return _portName; }
        }

        #endregion

        #region Events

        #region "Hàm test send dữ liệu"

        public bool IsRandomError = false; // Tạo ra lỗi dữ liệu test ngẫu nhiên
        /// <summary>
        /// HT Code: Khởi tạo chuỗi lệnh để gửi qua cổng COM
        /// Cấu trúc khởi tạo: "{[DATA]}\r"
        /// </summary>
        /// <param name="buff"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public char[] CreateDataPack(char[] buff, char[] data)
        {
            char[] start = new char[] { '{' };
            char[] end = new char[] { '}' };
            char[] newrow = new char[] { '\r' };

            //Copy ký tự '{' vào đầu buff
            start.CopyTo(buff, 0);
            //Copy dữ liệu vào đầu buff
            data.CopyTo(buff, 1);
            //Copy ký tự kết thúc data '}' vào cuối buff
            end.CopyTo(buff, buff.Length - 2);
            //Copy ký tự new row '\r' vào cuối buff
            newrow.CopyTo(buff, buff.Length - 1);
            return buff;
        }

        /// <summary>
        /// Gửi thông tin gps theo string
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public char[] SendGPSData(string msg)
        {
            return SendGPSData(msg.ToCharArray());
        }

        /// <summary>
        /// Gửi thông tin gps theo mảng char[]
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public char[] SendGPSData(char[] data)
        {
            char[] buff = new char[GeneralClass.FULL_DATA_LEN];

            buff = CreateDataPack(buff, data);

            //Convert sang bytes
            byte[] buffBytes = Encoding.ASCII.GetBytes(buff);
            try
            {
                //Nếu tạo dữ liệu test bị lỗi
                if (IsRandomError)
                {
                    //===========================================
                    //Tạo byte lỗi ngẫu nhiên
                    Random r = new Random();
                    int isCreateFailData = r.Next(1, 6);
                    //Nếu rơi vào trường hợp 5 sẽ tạo byte bị lỗi
                    if (isCreateFailData == 4)
                    {
                        //Chọn ngẫu nhiên vị trí sẽ bị lỗi
                        int index = r.Next(0, GeneralClass.FULL_DATA_LEN - 1);
                        buffBytes[index] = (byte) (buffBytes[index] >> 1); //dịch phải 1 byte
                    }
                }

                //===========================================
                //Send data
                WriteBytes(buffBytes, 0, buffBytes.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Xảy ra lỗi khi kết nối:\n" + ex.Message, "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return buff;
        }

        #endregion

        //Event dùng nhận dữ liệu mới nhất
        public SerialDataReceivedEventHandler comPort_DataReceivedEvent;
        //Event dùng cho hiển thị kết quả log
        public SerialDataReceivedEventHandler comPort_DataReceivedEventLog;
        //Event dùng xử lý thông tin gps nhận được
        public SerialDataReceivedEventHandler comPort_DataReceivedEventProcessor;
        public void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string log = "";
            int length = 0; // Số bytes đọc được từ cổng COM
            
            try
            {
                //Nếu nhận dữ liệu đầu tiên ngay sau khi mở cổng COM
                if (isFirstReceivedData)
                {
                    isFirstReceivedData = false;
                }

                //Nếu kích thước dữ liệu buffer >= kích thước gói dữ liệu gps cần đọc
                if (serialPort.BytesToRead >= GeneralClass.FULL_DATA_LEN)
                {
                    isComplete = false;
                    //============================================================
                    //Bước 1: Tính toán số byte thực sự cần đọc từ cổng COM
                    //Nếu BytesToRead + lenBuff mà lớn hơn MAX_LENGTH của comBuff
                    if ((serialPort.BytesToRead + lenBuff) > MAX_LENGTH) 
                    {
                        //Chỉ đọc length = số byte còn lại thỏa điều kiện sao cho length + lenBuff không vượt quá kích thước MAX_LENGTH của comBuff
                        length = MAX_LENGTH - lenBuff;
                    }
                    else
                    {
                        //Nếu không thì đọc hết số byte trong cổng COM
                        length = serialPort.BytesToRead;
                    }
                    
                    //Đọc dữ liệu từ cổng COM theo số byte cần đọc là length
                    //Nếu length > 0 thì mới đọc từ cổng COM, không thì tiếp tục xử lý comBuff
                    if (length > 0)
                    {
                        //Đọc dữ liệu từ cổng COM vào buffer comBuff tại vị trí lenBuff
                        serialPort.Read(comBuff, lenBuff, length);
                        lenBuff += length; //số byte đọc từ cổng COM chưa xử lý xong
                    }

                    //=====================================
                    //Hiển thị dữ liệu buffer đã đọc ra log
                    log = string.Format("[{0}] [ComBuffer] [Len:{1}] {2}", DateTime.Now, lenBuff, Encoding.ASCII.GetString(comBuff, 0, lenBuff));
                    ListMessages.Add(log);
                    if (comPort_DataReceivedEventLog != null)
                        comPort_DataReceivedEventLog(sender, e);
                    //============================================================

                    //============================================================
                    //Bước 2: Đọc dữ liệu trong mảng comBuff theo kích thước gói dữ liệu GeneralClass.FULL_DATA_LEN
                    string tmpMsg = "";
                    int startIndex = -1;
                    int endIndex = -1;
                    int index = 0; //Vị trí index trong mảng comBuff
                    //Xử lý vòng lặp trong khi lenBuff - index >= FULL_DATA_LEN byte thì tiếp tục đọc
                    while (lenBuff - index >= GeneralClass.FULL_DATA_LEN)
                    {
                        //Nếu vị trí i chứa ký tự đầu chuỗi dữ liệu từ GPS '{'
                        if (comBuff[index] == '{')
                        {
                            //Đọc hết FULL_DATA_LEN byte theo dạng chuỗi
                            tmpMsg = Encoding.ASCII.GetString(comBuff, index, GeneralClass.FULL_DATA_LEN);
                            
                            //=====================================
                            //Hiển thị kết quả ra log
                            log = string.Format("[{0}] [Data] {1}", DateTime.Now, tmpMsg);
                            ListMessages.Add(log);
                            if (comPort_DataReceivedEventLog != null)
                                comPort_DataReceivedEventLog(sender, e);

                            byte[] buff = Encoding.ASCII.GetBytes(tmpMsg);
                            //Kiểm tra xem cuối chuỗi có đúng là ký tự kết thúc chuỗi không '\r'
                            if (buff[buff.Length - 1] == '\r')
                            {
                                if (tmpMsg.StartsWith("{"))
                                {
                                    //=====================================
                                    //Xử lý dữ liệu
                                    startIndex = tmpMsg.IndexOf("{");
                                    endIndex = tmpMsg.IndexOf("}");
                                    if (endIndex > startIndex)
                                    {
                                        GeneralClass.GPSData = tmpMsg.Substring(startIndex + 1, endIndex - startIndex - 1);
                                        //Xử lý dữ liệu gps
                                        if (comPort_DataReceivedEventProcessor != null)
                                            comPort_DataReceivedEventProcessor(sender, e);
                                    }
                                }
                            }
                            //Tăng index sau khi đọc xong chuỗi dữ liệu GPS
                            index += GeneralClass.FULL_DATA_LEN;
                        }
                        else
                        {
                            index++;
                        }
                    }
                    
                    //Xử lý dữ liệu còn lại nếu chưa đủ byte cần nhận
                    //Bằng cách chuyển số byte còn lại về đầu mảng comBuff
                    lenBuff -= index;
                    if (lenBuff > 0)
                    {
                        byte[] tempDataByte;
                        tempDataByte = new byte[lenBuff];
                        //Đọc hết dữ liệu còn lại của comBuff
                        Array.Copy(comBuff, index, tempDataByte, 0, lenBuff);
                        //Khởi tạo lại mảng comBuff
                        comBuff = new byte[MAX_LENGTH];
                        //Copy vào đầu mảng byte comBuff
                        Array.Copy(tempDataByte, comBuff, tempDataByte.Length);
                        //=====================================
                        //Hiển thị kết quả ra log
                        log = string.Format("[{0}] [RemainBuffer] {1}", DateTime.Now, Encoding.ASCII.GetString(tempDataByte));
                        ListMessages.Add(log);
                        if (comPort_DataReceivedEventLog != null)
                            comPort_DataReceivedEventLog(sender, e);
                    }

                    //Hiển thị kết quả lên bản đồ sau một lần đọc dữ liệu từ cổng COM
                    if (comPort_DataReceivedEvent != null)
                        comPort_DataReceivedEvent(sender, e);

                    isComplete = true;
                    if (isClose)
                    {
                        comPortClose();
                    }
                }
            }
            catch (System.OutOfMemoryException ex)
            {
                //=====================================
                //Hiển thị kết quả ra log
                log = string.Format("[{0}] [OutOfMemoryException] {1} - {2}", DateTime.Now, ex.Message, ex.StackTrace);
                ListMessages.Add(log);
                if (comPort_DataReceivedEventLog != null)
                    comPort_DataReceivedEventLog(sender, e);
                serialPort.DiscardInBuffer();
            }
            catch (Exception ex)
            {
                //=====================================
                //Hiển thị kết quả ra log
                log = string.Format("[{0}] [Error Exception] {1} - {2}", DateTime.Now, ex.Message, ex.StackTrace);
                ListMessages.Add(log);
                if (comPort_DataReceivedEventLog != null)
                    comPort_DataReceivedEventLog(sender, e);
                serialPort.DiscardInBuffer();
            }
        }

        //Event dùng cho hiển thị lỗi ra log
        public SerialErrorReceivedEventHandler comPort_SerialErrorReceivedEvent;
        public void comPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            //=====================================
            //Hiển thị kết quả ra log
            string log = string.Format("[{0}] [ErrorReceived] {1}", DateTime.Now, "Lỗi truyền tin");
            ListMessages.Add(log);
            if (comPort_SerialErrorReceivedEvent != null)
                comPort_SerialErrorReceivedEvent(sender, e);

            //Xóa dữ liệu buff đang có
            serialPort.DiscardInBuffer();
            serialPort.Close();
            //buffer = null;
        }

        public void WriteBytes(byte[] buff, int offset, int length)
        {
            if (serialPort.IsOpen)
            {
                try
                {
                    serialPort.Write(buff, offset, length);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        #endregion

        #region Comport Methods
        public bool IsOpen()
        {
            return serialPort.IsOpen;
        }

        public void comPortOpen()
        {
            try
            {
                if (!serialPort.IsOpen)
                {
                    ListMessages = new List<string>();
                    isFirstReceivedData = true;
                    isClose = false;
                    comBuff = new byte[MAX_LENGTH];
                    lenBuff = 0;
                    serialPort.PortName = _portName;
                    serialPort.BaudRate = int.Parse(_baudRate);
                    serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), _parity);
                    serialPort.DataBits = int.Parse(_dataBits);
                    serialPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), _stopBits);
                    serialPort.Open();
                    //Nếu mở cổng COM mà chưa có dữ liệu truyền về thì xóa dữ liệu có sẵn
                    if (isFirstReceivedData)
                    {
                        serialPort.DiscardInBuffer();
                        serialPort.DiscardOutBuffer();
                        isFirstReceivedData = false;
                    }
                }
                else
                {
                    if (MessageBox.Show("Port đã sẵn sàng mở!\nBạn cần đóng nó trước khi mở trở lại!\nBạn có muốn thực hiện đóng kết nối hiện tại và mở lại kết nối này hay không?", "Thông báo", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                    {
                        //Ngắt và mở lại kết nối
                        serialPort.Close();
                        comPortOpen();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private bool isClose = false;
        private bool isComplete = true;
        public void comPortClose()
        {
            try
            {
                //Close the Port
                isClose = true;
                if (serialPort.IsOpen && isClose && isComplete)
                {
                    serialPort.DiscardInBuffer();
                    serialPort.DiscardOutBuffer();
                    serialPort.Close();
                }
                
                //buffer = null;
                //count = 0;
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Lỗi đóng cổng COM: {0} {1}", ex.Message, ex.StackTrace));
            }
            //finally
            //{
            //    //if (!serialPort.IsOpen)
            //    //    serialPort.Dispose();
            //}
        }

        public List<string> getListPortName()
        {
            //lấy danh sách cổng COM có trên máy
            List<string> portNames = new List<string>();
            foreach (string c in SerialPort.GetPortNames())
            {
                portNames.Add(c);
            }
            return portNames;
        }

        public List<string> getListStopBit()
        {
            //lấy danh sách Stopbits
            List<string> stopBits = new List<string>();
            foreach (string c in Enum.GetNames(typeof(StopBits)))
            {
                if (c != "None")
                    stopBits.Add(c);
            }
            return stopBits;
        }

        public List<string> getListParity()
        {
            //lấy danh sách Parity
            List<string> parity = new List<string>();
            foreach (string c in Enum.GetNames(typeof(Parity)))
            {
                parity.Add(c);
            }
            return parity;
        }

        public List<string> getListBaudrate()
        {
            string[] lstBaudRate = { "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200" };
            List<string> baudRates = new List<string>();
            foreach (string c in lstBaudRate)
            {
                baudRates.Add(c);
            }
            return baudRates;
        }

        public List<string> getListDataBits()
        {
            string[] lstDataBits = { "7", "8", "9" };
            List<string> dataBits = new List<string>();
            foreach (string c in lstDataBits)
            {
                dataBits.Add(c);
            }
            return dataBits;
        }

        public enum DefaultSettings
        {
            COM1,
            BaudRate = 9600,
            None,
            DataBit = 8
        }

        public void getDefaultSettings(ref string _BaudRate, ref string _StopBit, ref string _DataBit, ref string _Parity)
        {
            _baudRate = "9600";
            _StopBit = "None";
            _DataBit = "8";
            _parity = "None";
        }
        #endregion

    }
}

